#include "snmpinc.h"
#if SNMP_WANTED==1
/****************************************************************************
NAME:  A_DecodeTypeValue

PURPOSE:  Decode the numeric part of an ASN.1 type from a stream.
          The data stream is read using the local I/O package.
          On exit, the stream poINT32er will be positioned to the byte
          *AFTER* the type.

NOTE:     The Class portion of the type is NOT decoded here, only the
          value portion.
          The user should call A_DecodeTypeClass *BEFORE* calling this
          routine in order to get the class.

PARAMETERS:  LCL_FILE *     A stream descriptor (already open)
             _INT16 *          Receives an error code, if any.

RETURNS:  _UINT16         The type value

RESTRICTIONS:  It is assumed that the stream does not reach EOF before the
                end of the field.
****************************************************************************/
_UINT16 A_DecodeTypeValue(LCL_FILE *lfile, _INT16 *errp)
{
	_UINT8 oct;
	oct = (_UINT8)(Lcl_Getc(lfile) & ~A_IDCF_MASK);
	if(Lcl_Eof(lfile))
	{
		*errp = AE_PREMATURE_END;
		return (_UINT16) 0;
	}
	if(oct != 0x1F)
	{
		/* Are there extension bytes? */
		/* No extensions, type is in oct */
		return (_UINT16) oct;
	}
	else
	{
		/* Type is in extension octets */
		_UINT16 t = 0;
		for(;;)
		{
			oct = (_UINT8) Lcl_Getc(lfile);
			if(Lcl_Eof(lfile))
			{
				*errp = AE_PREMATURE_END;
				return t;
			}
			if(!(oct & 0x80))
			{
				break;    /* Hit final byte, we'll use */
			}
			/* it at the end of the loop */
			t |= (_UINT16)(oct & 0x7F);	/* Deal with a non-final byte */
			t <<= 7;
		}
		t |= (_UINT16) oct;	/* Take care of the final byte (the one */
		/* without the 0x80 continuation bit.)  */
		return t;
	}
	/*NOTREACHED*/
}

/****************************************************************************
NAME:  A_DecodeLength

PURPOSE:  Decode an ASN.1 length from a stream.
          The data stream is read using the local I/O package.
          On exit, the stream poINT32er will be positioned to the byte
          *AFTER* the length.

PARAMETERS:  LCL_FILE *     Stream descriptor
             _INT16 *          Receives an error code, if any.

RETURNS:  _UINT16 -- the length.
          If the length is indefinite, (_UINT16)-1 is returned.

RESTRICTIONS:  The stream must be open.
               It is assumed that the stream will not reach EOF before the
               length is decoded.
****************************************************************************/
_UINT16 A_DecodeLength(LCL_FILE *lfile, _INT16 *errp)
{
	_UINT8 oct=0;
	_UINT8 lsize=0;
	_UINT16 len = 0;
	oct = (_UINT8) Lcl_Getc(lfile);
	if(Lcl_Eof(lfile))
	{
		*errp = AE_PREMATURE_END;
		return (_UINT16) 0;
	}
	/* Indefinite form? */
	if(oct == 0x80)
	{
		*errp = AE_INDEFINITE_LENGTH;
		return (_UINT16) - 1;
	}
	if(!(oct & 0x80))			/* Short or long format? */
	{
		return (_UINT16) oct;    /* Short format */
	}
	else
	{
		/* Long format */
		lsize = oct & (_UINT8) 0x7F;	/* Get # of bytes comprising length field */
		while(lsize-- != 0)
		{
			len <<= 8;
			len |= (_UINT8) Lcl_Getc(lfile);
			if(Lcl_Eof(lfile))
			{
				*errp = AE_PREMATURE_END;
				return (_UINT16) 0;
			}
		}
		return len;
	}
	/*NOTREACHED*/
}

/********************
A_DecodeOctetStringData

PURPOSE:  Pull an octet string from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the data field.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        _UINT16       Length of octet string, from its ASN.1 header
        _UINT8 *     Control structure to receive the data.
        _INT16 *           Receives an error code, if any.

Returns: Nothing

Note:   On return, the "start_bp" component of the buffer structure
        poINT32s to a "malloc"-ed area in which the octet string is held.
        Note that the octet string is NOT null terminated, may contain
        INT32ernal nulls. A null poINT32er, (char *)0, is used if no area
        is malloc-ed.
        If the string is of zero length, a dummy buffer is established
        which appears to be have a static buffer of length zero at
        address zero.
********************/
void A_DecodeOctetStringData(LCL_FILE *stream, _UINT16 length,
                             _UINT8 *ebuffp,
                             _UINT16 maxEbuffLen,
                             _INT16 *errp)
{
	_UINT16 got;
	/*	_UINT8 c=0;*/
	*errp=0;
	if((length != 0) && (length != (_UINT16) - 1))
	{
		if(length>maxEbuffLen) /*new add*/
		{
			(void)Lcl_Read(stream, ebuffp,maxEbuffLen);
			got=(length-maxEbuffLen);
			while(got > 0)
			{
				/*c=Lcl_Getc(stream);*/
				Lcl_Getc(stream);
				if(Lcl_Eof(stream))
				{
					break;
				}
				--got;
			}
		}
		else
		{
			got=Lcl_Read(stream, ebuffp, length);
		}
	}
}
/********************
A_DecodeOctetStringWTC

PURPOSE:  Pull an octet string from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the octet string's type field.
          This version does type checking.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        _UINT8 *     Control structure to receive the data.
        _INT16 *           Receives an error code, if any.
        _UINT16       The type value
        _UINT8         A_IDC_MASK flag values

Returns: Nothing

Note:   On return, the "start_bp" component of the buffer structure
        poINT32s to a "malloc"-ed area in which the octet string is held.
        Note that the octet string is NOT null terminated, may contain
        INT32ernal nulls. A null poINT32er, (char *)0, is used if no area
        is malloc-ed.
********************/
void A_DecodeOctetStringWTC(LCL_FILE *stream,
                            _UINT8 *ebuffp, _UINT16 maxLen,
                            _INT16 *errp, _UINT16 id, _UINT8 flags)
{
	_UINT16 os_length;
	if((A_DecodeTypeClass(stream) != flags) || (A_DecodeTypeValue(stream, errp) != id))
	{
		if(*errp == 0)
		{
			*errp = AE_WRONG_TYPE;
		}
		/* On a decoding error, pretend we have a zero length string */
		return;
	}
	os_length = A_DecodeLength(stream, errp);
	if(os_length>maxLen)
	{
		if(*errp == 0)
		{
			*errp = AUTHORIZATION_ERROR;
		}
		return;
	}
	if(*errp == 0)
	{
		A_DecodeOctetStringData(stream,os_length,ebuffp,maxLen,errp);
	}
}


/********************
A_DecodeIntegerData

PURPOSE:  Pull an INT32eger from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the data field.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        _UINT16       Length of contents field, from the ASN.1 header
        _INT16 *           Receives an error code, if any.

Returns: _INT16       (See note below)

NOTE: If the received value is really unsigned, then the caller should
merely cast the value returned by this procedure to an _UINT32.

WARNING: If the INT32eger occupies more than 4 octets, then high order precision
will be lost, including the sign bit.  For unsigned values in which the
basic value occupies all 4 octets, the sign octet, containing a zero sign
bit, will be lost but will not damage the returned value.
********************/
_INT32 A_DecodeIntegerData(LCL_FILE *stream, _UINT16 length, _INT16 *errp)
{
	_INT32 ivalue = 0;
	_INT32 firstone = 1;
	_UINT8 oct;
	while(length-- != 0)
	{
		oct = (_UINT8) Lcl_Getc(stream);
		if(Lcl_Eof(stream))
		{
			*errp = AE_PREMATURE_END;
			return ivalue;
		}
		/* See whether we are receiving something that has the sign bit set, or
		 * if this is a 5 byte unsigned _INT32 check the first byte, it must be 0 */
		if(firstone)
		{
			firstone = 0;
			if((length == 4) && (oct != 0))
			{
				*errp = AE_WRONG_VALUE;
				return ivalue;
			}
			if(oct & (_UINT8) 0x80)
			{
				ivalue = (_INT32) - 1;
			}
		}
		/*lINT32 -e703    */
		ivalue <<= 8;
		/*lINT32 +e703    */
		ivalue |= oct;			/* 'oct' better not be sign extended!!! */
	}
	return ivalue;
}

/********************
A_DecodeIntegerWTC

PURPOSE:  Pull an INT32eger from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the INT32eger's ASN.1 type field.
          As we pull off the class and type we check them against
          the arguments to make sure they are correct.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        _INT16 *           Receives an error code, if any.
        _UINT16       The type value
        _UINT8         A_IDC_MASK flag values

Returns: _INT32;
********************/
_INT32 A_DecodeIntegerWTC(LCL_FILE *stream, _INT16 *errp, _UINT16 id, _UINT8 flags)
{
	_UINT16 INT32_length;
	if((A_DecodeTypeClass(stream) != flags) || (A_DecodeTypeValue(stream, errp) != id))
	{
		if(*errp == 0)
		{
			*errp = AE_WRONG_TYPE;
		}
		return (0);
	}
	INT32_length = A_DecodeLength(stream, errp);
	if(*errp == 0)
	{
		if(INT32_length > 5)
		{
			*errp = AE_WRONG_VALUE;
		}
		else
		{
			return A_DecodeIntegerData(stream, INT32_length, errp);
		}
	}
	return (0);
}

/********************
A_DecodeObjectIdData

PURPOSE:  Pull an object identifier from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the data field.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        _UINT16       Length of contents field, from the ASN.1 header
        OBJ_ID_T *      Object identifier structure to receive the object id.
                        The "component_list" will be "malloc"ed.
                        component_list == (_UINT32 *)0 indicates that
                        there is no component_list.
        _INT16 *           Receives an error code, if any.

Returns: Nothing
********************/
void A_DecodeObjectIdData(LCL_FILE *stream, _UINT16 length, OBJ_ID_T *objp, _INT16 *errp)
{
	_UINT16 content_offset;	/* Offset in stream where contents begins */
	_INT16 subids=0;
	_INT32 left;					/* Count of unused contents bytes */
	_UINT16 j=0;					/* Number of subidentifiers     */
	_INT16 subid_num;
	_UINT32 subid_val;			/* Value of a subidentifier */
	_UINT32 cp[MAX_OID_LENGTH];
	_UINT8 c, first_c;
	_INT16 subid_bytes;	/* number of bytes in this sub id */
	_INT16 subid_done;	/* flag to see if the sub id is finished */
	objp->num_components = 0;
	(void)memset((_UINT8 *)(&cp[0]),0,sizeof(cp));
	/* Remember where the contents begins */
	content_offset = Lcl_Tell(stream);
	/* Count the number of components */
	for(subids = 0, left = (_INT32) length; left > 0;)
	{
		first_c = (_UINT8) Lcl_Getc(stream);
		if(Lcl_Eof(stream))
		{
			*errp = AE_PREMATURE_END;
			return;
		}
		left--;
		/* if its not the last (only) byte, step through the rest of the bytes */
		if(first_c & 0x80)
		{
			for(subid_bytes = 2, subid_done = 0; left; subid_bytes++)
			{
				c = (_UINT8) Lcl_Getc(stream);
				if(Lcl_Eof(stream))
				{
					*errp = AE_PREMATURE_END;
					return;
				}
				left--;
				if((c & 0x80) == 0)
				{
					subid_done = 1;
					break;
				}
			}
			if((subid_done == 0) || (subid_bytes > 5) || ((subid_bytes == 5) && (first_c > 0x8f)))
			{
				*errp = AE_WRONG_LENGTH;
				return;
			}
		}
		/* finally update the count of the subids */
		subids++;
	}
	(void) Lcl_Seek(stream, content_offset, 0);
	/* Null object id if no subidentifier fields */
	if(subids == 0)
	{
		return;
	}
	objp->num_components = subids + 1;
	/* Decode the subids and components */
	j=0;
	for(subid_num = 0; subid_num < subids; subid_num++)
	{
		/* Decode the subidentifier */
		for(subid_val = 0;;)
		{
			c = (_UINT8) Lcl_Getc(stream);
			if(Lcl_Eof(stream))
			{
				*errp = AE_PREMATURE_END;
				objp->num_components = 0;
				return;
			}
			subid_val <<= 7;
			subid_val |= (_UINT32)(c & 0x7F);
			if(!(c & 0x80))
			{
				break;
			}
		}
		/* Is this the first subidentifier?                 */
		/* i.e. the one that contains TWO components?       */
		if(subid_num == 0)		/*#the first byte is 0x2b,decoded to 1.3#*/
		{
			if(subid_val < 40)
			{
				cp[j++]= 0;
				cp[j++]= subid_val;
			}
			else
			{
				if(subid_val < 80)
				{
					cp[j++]= 1;
					cp[j++]= subid_val - 40;
				}
				else
				{
					cp[j++] = 2;
					cp[j++] = subid_val - 80;
				}
			}
		}
		else
		{
			/* subid_num != 0, i.e. this is not the first subidentifier */
			cp[j++]= subid_val;
		}
	}
	for(j=0; j<objp->num_components; j++)
	{
		objp->component_list[j]=cp[j];
	}
	return;
}

/********************
A_DecodeObjectIdWTC
PURPOSE:  Pull an object identifer from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the object identifier's ASN.1 type field.
          This version does type checking
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.
Parameters:
        LCL_FILE *      Stream descriptor
        OBJ_ID_T *      Object identifier structure to receive the object id.
                        The "component_list" will be "malloc"ed.
                        component_list == (_UINT32 *)0 indicates that
                        there is no component_list.
        _INT16 *           Receives an error code, if any.
        _UINT16       The type value
        _UINT8         A_IDC_MASK flag values
Returns: Nothing
********************/

void A_DecodeObjectIdWTC(LCL_FILE *stream, OBJ_ID_T *objp, _INT16 *errp, _UINT16 id, _UINT8 flags)
{
	_UINT16 length;
	if((A_DecodeTypeClass(stream) != flags) || (A_DecodeTypeValue(stream, errp) != id))
	{
		if(*errp == 0)
		{
			*errp = AE_WRONG_TYPE;
		}
		return;
	}
	length = A_DecodeLength(stream, errp);
	if(*errp == 0)
	{
		A_DecodeObjectIdData(stream, length, objp, errp);
	}
	return;
}

/* We only install ui64 routine if the type is installed */
/********************
A_DecodeInteger64Data

PURPOSE:  Pull up to a 64 bit INT32eger from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the data field.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        _UINT16       Length of contents field, from the ASN.1 header
        _UINT64_T *     Space for the received _INT16
        _INT16 *           Receives an error code, if any.

Returns: void

WARNING: If the INT32eger occupies more than 8 octets, then high order precision
will be lost, including the sign bit.  For unsigned values in which the
basic value occupies all 8 octets, the sign octet, containing a zero sign
bit, will be lost but will not damage the returned value.
********************/
void A_DecodeInteger64Data(LCL_FILE *stream, _UINT16 length, UINT64_T *value, _INT16 *errp)
{
	_INT32 firstone = 1;
	_UINT8 oct;
	value->high = value->low = 0;
	while(length-- != 0)
	{
		oct = (_UINT8) Lcl_Getc(stream);
		if(Lcl_Eof(stream))
		{
			*errp = AE_PREMATURE_END;
			return;
		}
		/* See whether we are receiving something that has the sign bit set */
		if(firstone)
		{
			firstone = 0;
			if((length == 8) && (oct != 0))
			{
				*errp = AE_WRONG_VALUE;
				return;
			}
			if(oct & (_UINT8) 0x80)
			{
				value->high = (_UINT32) 0xFFFFFFFFL;
				value->low = (_UINT32) 0xFFFFFFFFL;
			}
		}
		/* Decide which of the two words to put the octet INT32o   */
		/* if length >= 4 we are working on the high order bytes */
		/* equal sign is because we already decremented length   */
		if(length >= 4)
		{
			value->high <<= 8;
			value->high |= oct;	/* 'oct' better not be sign extended!!! */
		}
		else
		{
			value->low <<= 8;
			value->low |= oct;	/* 'oct' better not be sign extended!!! */
		}
	}
	return;
}

/********************
A_DecodeInteger64

PURPOSE:  Pull up to a 64 bit INT32eger from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the INT32eger's ASN.1 type field.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        _UINT64 *     Space to receive the _INT16
        _INT16 *           Receives an error code, if any.

Returns: _INT32;
********************/
#if 0
void A_DecodeInteger64(LCL_FILE *stream, UINT64_T *value, _INT16 *errp)
{
	_UINT16 INT32_length;
	(void) A_DecodeTypeValue(stream, errp);
	INT32_length = A_DecodeLength(stream, errp);
	if(*errp == 0)
	{
		if(INT32_length > 9)
		{
			*errp = AE_WRONG_VALUE;
		}
		else
		{
			A_DecodeInteger64Data(stream, INT32_length, value, errp);
		}
	}
	return;
}
#endif

/********************
A_DecodeOctetWTC

PURPOSE:  Pull a single octet from an ASN.1 stream
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the octet string's type field.
          This version does type checking.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.
          An error will be generated if the octet string is longer than 1.

Parameters:
        LCL_FILE *      Stream descriptor
        _INT16 *           Receives an error code, if any.
        _UINT16       The type value
        _UINT8         A_IDC_MASK flag values

Returns: _UINT8 the octet from the stream.
********************/
#if 0
_UINT8 A_DecodeOctetWTC(LCL_FILE *stream, _INT16 *errp, _UINT16 id, _UINT8 flags)
{
	_UINT8 buff;
	if((A_DecodeTypeClass(stream) != flags) || (A_DecodeTypeValue(stream, errp) != id))
	{
		if(*errp == 0)
		{
			*errp = AE_WRONG_TYPE;
		}
		return (0);
	}
	if((A_DecodeLength(stream, errp) != 1) || *errp)
	{
		if(*errp == 0)
		{
			*errp = AE_WRONG_LENGTH;
		}
		return (0);
	}
	if(Lcl_Read(stream, &buff, 1) != 1)
	{
		*errp = AE_WRONG_LENGTH;
		return (0);
	}
	return (buff);
}
#endif

/********************
A_DecodeInteger

PURPOSE:  Pull an INT32eger from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the INT32eger's ASN.1 type field.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        _INT16 *           Receives an error code, if any.

Returns: _INT32;
********************/
#if 0
_INT32 A_DecodeInteger(LCL_FILE *stream, _INT16 *errp)
{
	_UINT16 INT32_length;
	(void) A_DecodeTypeValue(stream, errp);
	INT32_length = A_DecodeLength(stream, errp);
	if(*errp == 0)
	{
		if(INT32_length > 5)
		{
			*errp = AE_WRONG_VALUE;
		}
		else
		{
			return A_DecodeIntegerData(stream, INT32_length, errp);
		}
	}
	return (0);
}
#endif
/********************
A_DecodeObjectId

PURPOSE:  Pull an object identifer from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the object identifier's ASN.1 type field.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        OBJ_ID_T *      Object identifier structure to receive the object id.
                        The "component_list" will be "malloc"ed.
                        component_list == (_UINT32 *)0 indicates that
                        there is no component_list.
        _INT16 *           Receives an error code, if any.

Returns: Nothing
********************/
#if 0
void A_DecodeObjectId(LCL_FILE *stream, OBJ_ID_T *objp, _INT16 *errp)
{
	_UINT16 leng;
	(void) A_DecodeTypeValue(stream, errp);
	leng = A_DecodeLength(stream, errp);
	if(*errp == 0)
	{
		A_DecodeObjectIdData(stream, leng, objp, errp);
	}
	return;
}
#endif

/********************
A_DecodeOctetString

PURPOSE:  Pull an octet string from an ASN.1 stream.
          The data stream is read using the local I/O package.
          On entry stream poINT32er should be positioned to the first byte
          of the octet string's type field.
          On exit, the stream poINT32er will be positioned to at the start
          of the next ASN.1 type field.

Parameters:
        LCL_FILE *      Stream descriptor
        _UINT8 *     Control structure to receive the data.
        _INT16 *           Receives an error code, if any.

Returns: Nothing

Note:   On return, the "start_bp" component of the buffer structure
        poINT32s to a "malloc"-ed area in which the octet string is held.
        Note that the octet string is NOT null terminated, may contain
        INT32ernal nulls. A null poINT32er, (char *)0, is used if no area
        is malloc-ed.
********************/
#if 0
void A_DecodeOctetString(LCL_FILE *stream, _UINT8 *ebuffp, _INT16 *errp)
{
	_UINT16 os_length;
	(void) A_DecodeTypeValue(stream, errp);
	os_length = A_DecodeLength(stream, errp);
	if(*errp == 0)
	{
		A_DecodeOctetStringData(stream, os_length, ebuffp, errp);
	}
}
#endif

#endif
